import {GraphicsExt as Graphics} from "../common/graphics-ext";
import {ChessModel} from "../model/chess-model";
import {Text, TextStyle} from '@pixi/text';
import {Container} from '@pixi/display'
import {Base} from "./base";
import {Game} from "../game";
import {Expo, gsap} from "gsap";
import {Back, Elastic} from "../common/animation";
import {collect, emit} from "../common/runner-factory";
import {EmitAfter, funcProxy, SetCall} from "../common/decorator";
import {Color} from "../const";

export interface IChess {
  x: number,
  y: number,
  attack(chess: IChess);
  get model(): ChessModel;
}

export class Chess extends Base implements IChess {
  public static readonly WIDTH = 100;
  public static readonly HEIGHT = 160;
  private static readonly TXT_MARGIN = 20;

  private hpText !: Text;
  private atkText !: Text;
  private effectLayer: Container;

  private trembleFunc: any;
  public team: string;

  constructor(model?:ChessModel) {
    super(model);
  }

  getProxyHandler(): ProxyHandler<this> {
    return {
      set: this.set,
      get(target: Chess, p: string | symbol, receiver: any): any {
        if (p in target) {
          const origin = Reflect.get(target, p, receiver)
          return origin;
        }
        return function () {
          target.__call(p.toString());
        }
      }
    };
  }

  set(target: Chess, p: string | symbol, value: any, receiver: any): boolean {
    const old = target[p];
    Reflect.set(target, p, value, receiver)
    switch (p) {
      case 'model':
      case '_model':
        target.updateView()
        break
      case 'hp':
        if (value < old) {
          emit('showDamage', target, old - value)
        }
        target.updateView()
        break
      case 'atk':
        target.updateView()
        break
    }
    return true;
  }

  funcProxy(func:Function) {
    return new Proxy(func, {
      apply(target: Function, thisArg: any, argArray: any[]): any {
        console.log(target, thisArg, argArray);
      }
    });
  }

  public get model(): ChessModel {
    return this.proxy._model as ChessModel
  }

  public set model(model) {
    this.proxy._model = model
  }

  // constructor(model ?: ChessModel) {
  //   super(model);
  // }

  // @SetCall('updateView')
  // public set model(model: ChessModel) {
  //   this._model = model;
  // }
  //
  // public get model(): ChessModel {
  //   return this._model as ChessModel;
  // }
  protected func;
  init() {
    // this.func = this.funcProxy(this.test);
    // this.func(1)

    // @ts-ignore
    this.proxy.abc()


    this.initBg();
    this.initViews();
    this.initEvents();
  }

  test(a) {

  }

  initBg() {
    this.bg = Chess.drawBg();
    this.addChild(this.bg);
    this.tint = 0x0000ff;
  }

  public tremble() {
    const x = this.x;
    const y = this.y;
    let i = 0;
    this.trembleFunc = () => {
      if (i++ % 3 == 0) {
        this.x = x + (0.5 - Math.random()) * 10;
        this.y = y + (0.5 - Math.random()) * 10;
      }
    }
    Game.ticker.add(this.trembleFunc);
  }

  public stopTremble() {
    Game.ticker.remove(this.trembleFunc);
  }

  private static getTextStyle(): TextStyle {
    return new TextStyle({
      fontFamily: 'Arial',
      fontSize: 25,
      fontWeight: 'bold',
      stroke: '#000000',
      strokeThickness: 3,
      fill: ['#ffffff'],
    });
  }

  private initHpText() {
    const txt = new Text('00', Chess.getTextStyle());
    txt.x = Chess.TXT_MARGIN - Chess.WIDTH / 2;
    txt.y = Chess.HEIGHT / 2 - 20;
    txt.anchor.set(0.5);
    this.addChild(txt);

    this.hpText = txt;
  }

  private initAtkText() {
    const txt = new Text('00', Chess.getTextStyle());
    txt.x = Chess.WIDTH / 2 - Chess.TXT_MARGIN;
    txt.y = Chess.HEIGHT / 2 - 20;
    txt.anchor.set(0.5);
    this.addChild(txt);
    this.atkText = txt;
  }

  initEffectLayer() {
    this.effectLayer = new Container();
    this.addChild(this.effectLayer);
  }

  initViews() {
    this.initHpText();
    this.initAtkText();
    this.initEffectLayer();
  }

  initEvents() {
    // collect(this, 'afterAttack');
  }

  updateView() {
    // console.log("update view===============")
    this.updateHp(this.model.hp)
    this.updateAtk(this.model.atk)
  }

  updateHp(val: number) {
    const initHp = this.model.initHp;
    this.hpText.style.fill = initHp > val ? [Color.RED] : (initHp == val ? [Color.WHITE] : [Color.GREEN]);
    this.hpText.text = val.toString(10);
  }

  updateAtk(val:number) {
    const initAtk = this.model.initAtk;
    this.atkText.style.fill = initAtk > val ? [Color.RED] : (initAtk == val ? [Color.WHITE] : [Color.GREEN]);
    this.atkText.text = val.toString(10);
  }

  set hp(val: number) {
    this.model.hp = val;
  }

  get hp(): number {
    return this.model.hp;
  }

  set atk(val: number) {
    this.model.atk = val;
  }
  get atk(): number {
    return this.proxy.model.atk;
  }

  private static drawBg(): Graphics {
    const g = new Graphics();
    g.lineStyle(1, 0xff0000);
    g.beginFill(0x00ffff);
    g.drawEllipse(0, 0, Chess.WIDTH >> 1, Chess.HEIGHT >> 1);
    g.endFill();
    return g;
  }

  public clone(): Chess {
    const model = new ChessModel(this.model.initHp, this.model.initAtk);
    return new Chess(model);
  }

  public async attack(chess: Chess) {
    if (chess.zIndex >= this.zIndex) {
      this.zIndex = chess.zIndex + 1;
    }
    const pos = {x: this.x, y: this.y};
    // console.log('pos point', pos);
    const atk = {
      x: chess.x + (this.x - chess.x)/8,
      y: chess.y + (this.y - chess.y)/8,
    }
    // console.log('atk point', atk);

    await gsap.to(this, {...atk, duration:0.4, ease:Back.easeIn});
    emit('afterAttack');
    await this.afterAttack(chess);
    // await Chess.afterAtk(this, chess);

    if (!this.model.isDead()) {
      await gsap.to(this, {...pos, duration: 0.3, ease:Expo.easeInOut});
    }
  }

  get w(): number {
    return Chess.WIDTH;
  }

  get h():number {
    return Chess.HEIGHT;
  }

  async afterAttack(chess:Chess) {
    this.proxy.hp -= chess.atk;
    chess.proxy.hp -= this.atk;

    await Chess.checkDeath(this, chess);
  }

  static async afterAtk(self:Chess, enemy:Chess) {
    enemy.proxy.hp -= self.atk;
    self.proxy.hp -= enemy.atk;

    await Chess.checkDeath(self, enemy);
  }

  static async checkDeath(...chesses:Chess[]) {
    const deads = chesses.filter(chess => chess.model.isDead());
    if (deads.length == 0) return;

    await Promise.all(deads.map(async dead => await dead.die()));
  }

  public async goDie() {
    await this.die()
  }

  @EmitAfter('chessDie')
  public async die() {
    await gsap.to(this, {duration:1, alpha:0, delay:.8});
  }

  public __call(method:string, ...args) {
    console.log('__call===', method);
  }

}

